﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell;

namespace Sharp.UnusedReferences.EventsListeners
{
	class VsFileChangeEvents : IVsFileChangeEvents, IDisposable
	{
		private IVsFileChangeEx fileChangeEx;
		private Dictionary<string, IVsFileChangeEventsSubscriber> pdwCookies = new Dictionary<string, IVsFileChangeEventsSubscriber>();

		private bool IsDisposed;
		private static volatile object Mutex = new object();

		public VsFileChangeEvents()
		{
			fileChangeEx = ServiceProvider.GlobalProvider.GetService(typeof(SVsFileChangeEx)) as IVsFileChangeEx;
			//Debug.Assert(fileChangeEx != null, "Could not get the IVsFileChangeEx object from the services exposed by this project");
			if(fileChangeEx == null)
			{
				throw new InvalidOperationException();
			}
		}

		#region IVsFileChangeEvents Members

		public int FilesChanged(uint cChanges, string[] rgpszFile, uint[] rggrfChange)
		{
			for(int intFileIndex = 0; intFileIndex < rgpszFile.Length; intFileIndex ++)
			{
				pdwCookies[rgpszFile[intFileIndex]].FileChanged(cChanges, rggrfChange[intFileIndex]);
			}
			return VSConstants.S_OK;
		}

		public int DirectoryChanged(string pszDirectory)
		{
			return VSConstants.S_OK;
		}

		#endregion

		public void Dispose()
		{
			Dispose(true);
			GC.SuppressFinalize(this);
		}

		protected virtual void Dispose(bool disposing)
		{
			if(!IsDisposed)
			{
				lock(Mutex)
				{
					if(disposing)
					{
						UnsubscribeAll();
					}
					IsDisposed = true;
				}
			}
		}

		//public void Subscribe(string filepath, _VSFILECHANGEFLAGS SubscriptionFlags)
		//{
		//    if(fileChangeEx != null && !String.IsNullOrEmpty(filepath) && !pdwCookies.ContainsKey(filepath))
		//    {
		//        uint cookie;
		//        lock(pdwCookies)
		//        {
		//            ErrorHandler.ThrowOnFailure(fileChangeEx.AdviseFileChange(filepath, (uint)(SubscriptionFlags), this, out cookie));
		//            pdwCookies.Add(filepath, cookie);
		//        }
		//    }
		//}
		public void Subscribe(IVsFileChangeEventsSubscriber Subscriber, _VSFILECHANGEFLAGS SubscriptionFlags)
		{
			if(fileChangeEx != null && !String.IsNullOrEmpty(Subscriber.FilePath) && !pdwCookies.ContainsKey(Subscriber.FilePath))
			{
				lock(pdwCookies)
				{
					uint pdwCookie;
					ErrorHandler.ThrowOnFailure(fileChangeEx.AdviseFileChange(Subscriber.FilePath, (uint)(SubscriptionFlags), this, out pdwCookie));
					Subscriber.pdwCookie = pdwCookie;
					pdwCookies.Add(Subscriber.FilePath, Subscriber);
				}
			}
		}

		public void Ignore(IVsFileChangeEventsSubscriber Subscriber, bool ignore)
		{
			if(fileChangeEx != null && !String.IsNullOrEmpty(Subscriber.FilePath) && pdwCookies.ContainsKey(Subscriber.FilePath))
			{
				lock(pdwCookies)
				{
					ErrorHandler.ThrowOnFailure(fileChangeEx.IgnoreFile(Subscriber.pdwCookie, Subscriber.FilePath, (ignore) ? 1 : 0));
				}
			}
		}

		//public void Unsubscribe(string filepath)
		//{
		//    UnsubscribeCore(filepath);
		//    pdwCookies.Remove(filepath);
		//}

		public void Unsubscribe(IVsFileChangeEventsSubscriber Subscriber)
		{
			UnsubscribeCore(Subscriber.FilePath);
			pdwCookies.Remove(Subscriber.FilePath);
		}

		//public void UnsubscribeCore(string filepath)
		//{
		//    if(fileChangeEx != null && !String.IsNullOrEmpty(filepath))
		//    {
		//        uint cookie;
		//        lock(pdwCookies)
		//        {
		//            if(pdwCookies.TryGetValue(filepath, out cookie))
		//            {
		//                ErrorHandler.ThrowOnFailure(fileChangeEx.UnadviseFileChange(cookie));
		//            }
		//        }
		//    }
		//}

		public void UnsubscribeCore(string FilePath)
		{
			if(fileChangeEx != null && !String.IsNullOrEmpty(FilePath) && pdwCookies.ContainsKey(FilePath))
			{
				lock(pdwCookies)
				{
					ErrorHandler.ThrowOnFailure(fileChangeEx.UnadviseFileChange(pdwCookies[FilePath].pdwCookie));
				}
			}
		}

		public void UnsubscribeAll()
		{
			lock(pdwCookies)
			{
				foreach(string filepath in pdwCookies.Keys)
				{
					UnsubscribeCore(filepath);
				}
				pdwCookies.Clear();
			}
		}
	}
}
